#include <Python.h>
#include <assert.h>
#include <stdint.h>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include "jit_assert.hpp"
#include "hir_instructions.hpp"
#include "hir_graphbuilder.hpp"
#include "hir_exceptions.hpp"
#include "hir_instrstorage.hpp"

namespace hir
{

#define ASSERT_OPERAND_VALID(operand) \
        assert(operand < storage_->size())

uint32_t GraphBuilder::commonAppend(std::unique_ptr<Instruction> & tmp,uint32_t bci)
{
    tmp->setBci(bci);
    tmp->setGraphId(storage_->graphId());
    storage_->push_back(tmp);
    return storage_->graphIdAndInc();
}

uint32_t GraphBuilder::commonAddUse(uint32_t beUsed,uint32_t user)
{
    Instruction * beUsedInstr = (*storage_)[beUsed];
    beUsedInstr->addUse(user);
    return user;
}

uint32_t GraphBuilder::loadConst(uint32_t oparg,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new LoadConst(oparg));
    return commonAppend(tmp,bci);
}
uint32_t GraphBuilder::loadFast(uint32_t oparg,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new LoadFast(oparg));
    return commonAppend(tmp,bci);
}

void GraphBuilder::storeFast(uint32_t operand,uint32_t oparg,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new StoreFast(operand,oparg));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

uint32_t GraphBuilder::unaryPositive(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new UnaryPositive(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    return commonAddUse(operand,newInst);
}

uint32_t GraphBuilder::unaryNegative(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new UnaryNegative(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    return commonAddUse(operand,newInst);
}

uint32_t GraphBuilder::unaryNot(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new UnaryNot(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    return commonAddUse(operand,newInst);
}
uint32_t GraphBuilder::unaryConvert(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new UnaryConvert(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    return commonAddUse(operand,newInst);
}
uint32_t GraphBuilder::unaryInvert(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new UnaryInvert(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    return commonAddUse(operand,newInst);
}

uint32_t GraphBuilder::binaryPower(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryPower(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binaryMultiply(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryMultiply(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binaryDivide(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryDivide(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binaryTrueDivide(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryTrue_divide(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binaryFloorDivide(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryFloor_divide(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binaryModulo(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryModulo(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binaryAdd(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryAdd(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binarySubtract(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinarySubtract(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);

}

uint32_t GraphBuilder::binarySubscr(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinarySubscr(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binaryLshift(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryLshift(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binaryRshift(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryRshift(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binaryAnd(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryAnd(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binaryXor(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryXor(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::binaryOr(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new BinaryOr(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

void GraphBuilder::listAppend(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new ListAppend(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
}

void GraphBuilder::setAdd(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new SetAdd(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::slice(uint32_t operand1,uint32_t operand2,uint32_t operand3,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    ASSERT_OPERAND_VALID(operand3);
    std::unique_ptr<Instruction> tmp(new Slice(operand1,operand2,operand3));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
    return commonAddUse(operand3,newInst);
}

uint32_t GraphBuilder::deleteSlice(uint32_t operand1,uint32_t operand2,uint32_t operand3,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    ASSERT_OPERAND_VALID(operand3);
    std::unique_ptr<Instruction> tmp(new DeleteSlice(operand1,operand2,operand3));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
    return commonAddUse(operand3,newInst);
}

uint32_t GraphBuilder::storeSubscr(uint32_t operand1,uint32_t operand2,uint32_t operand3,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    ASSERT_OPERAND_VALID(operand3);
    std::unique_ptr<Instruction> tmp(new StoreSubscr(operand1,operand2,operand3));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
    return commonAddUse(operand3,newInst);
}

uint32_t GraphBuilder::storeSlice(uint32_t operand1,uint32_t operand2,uint32_t operand3,uint32_t operand4,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    ASSERT_OPERAND_VALID(operand3);
    ASSERT_OPERAND_VALID(operand4);
    std::unique_ptr<Instruction> tmp(new StoreSlice(operand1,operand2,operand3,operand4));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
    commonAddUse(operand3,newInst);
    return commonAddUse(operand4,newInst);
}

void GraphBuilder::deleteSubscr(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new DeleteSubscr(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
}

void GraphBuilder::printExpr(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new PrintExpr(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

void GraphBuilder::printItemTo(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new PrintItemTo(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
}

void GraphBuilder::printItem(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new PrintItem(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

void GraphBuilder::printNewlineTo(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new PrintNewLineTo(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

void GraphBuilder::printNewline(uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new PrintNewLine());
    commonAppend(tmp,bci);
}

void GraphBuilder::raiseVarargs(uint32_t operand1, uint32_t operand2, uint32_t operand3,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    ASSERT_OPERAND_VALID(operand3);
    std::unique_ptr<Instruction> tmp(new RaiseStatment(operand1,operand2,operand3));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
    commonAddUse(operand3,newInst);
}

uint32_t GraphBuilder::loadLocals(uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new LoadLocals());
    return commonAppend(tmp,bci);
}

void GraphBuilder::returnValue(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new ReturnValue(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

void GraphBuilder::yieldValue(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new YieldValue(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

void GraphBuilder::execStatment(uint32_t operand1, uint32_t operand2, uint32_t operand3,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    ASSERT_OPERAND_VALID(operand3);
    std::unique_ptr<Instruction> tmp(new ExecStatement(operand1,operand2,operand3));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
    commonAddUse(operand3,newInst);
}

void GraphBuilder::popBlock(uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new PopBlock());
    commonAppend(tmp,bci);
}

void GraphBuilder::endFinally(uint32_t bci)
{
    // std::unique_ptr<Instruction> tmp(new EndFinally());
    // commonAppend(tmp,bci);
}

uint32_t GraphBuilder::buildClass(uint32_t operand1, uint32_t operand2, uint32_t operand3,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    ASSERT_OPERAND_VALID(operand3);
    std::unique_ptr<Instruction> tmp(new BuildClass(operand1,operand2,operand3));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
    return commonAddUse(operand3,newInst);
}

void GraphBuilder::storeName(uint32_t operand,uint32_t oparg,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new StoreName(operand,oparg));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

void GraphBuilder::deleteName(uint32_t oparg,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new DeleteName(oparg));
    commonAppend(tmp,bci);
}

uint32_t GraphBuilder::unpackSequence(uint32_t operand,uint32_t index,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new UnpackSequence(operand,index));
    uint32_t newInst = commonAppend(tmp,bci);
    return commonAddUse(operand,newInst);
}

void GraphBuilder::storeAttr(uint32_t operand1,uint32_t operand2,uint32_t opargName,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new StoreAttr(operand1,operand2,opargName));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
}

void GraphBuilder::deleteAttr(uint32_t operand,uint32_t opargName,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new DeleteAttr(operand,opargName));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

void GraphBuilder::storeGlobal(uint32_t operand,uint32_t opargName,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new StoreGlobal(operand,opargName));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

void GraphBuilder::deleteGlobal(uint32_t opargName,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new DeleteGlobal(opargName));
    commonAppend(tmp,bci);
}

uint32_t GraphBuilder::loadName(uint32_t opargName,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new LoadName(opargName));
    return commonAppend(tmp,bci);
}

uint32_t GraphBuilder::loadGlobal(uint32_t opargName,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new LoadGlobal(opargName));
    return commonAppend(tmp,bci);
}

void GraphBuilder::deleteFast(uint32_t opargName,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new DeleteFast(opargName));
    commonAppend(tmp,bci);
}

uint32_t GraphBuilder::loadClosure(uint32_t opargName,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new LoadClosure(opargName));
    return commonAppend(tmp,bci);
}

uint32_t GraphBuilder::loadDeref(uint32_t opargName,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new LoadDeref(opargName));
    return commonAppend(tmp,bci);
}

void GraphBuilder::storeDeref(uint32_t objectOperand,uint32_t opargName,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new StoreDeref(objectOperand,opargName));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(objectOperand,newInst);
}

uint32_t GraphBuilder::buildTuple(uint32_t opargCount,uint32_t * operands,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new BuildTuple);

    for(uint32_t i = 0; i < opargCount; ++i)
    {
        ASSERT_OPERAND_VALID(operands[i]);
        tmp->addOperand(operands[i]);
    }
    uint32_t newInst = commonAppend(tmp,bci);
    for(uint32_t i = 0; i < opargCount; ++i)
    {
        commonAddUse(operands[i],newInst);
    }
    return newInst;
}

uint32_t GraphBuilder::buildList(uint32_t opargCount,uint32_t * operands,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new BuildList);

    for(uint32_t i = 0; i < opargCount; ++i)
    {
        ASSERT_OPERAND_VALID(operands[i]);
        tmp->addOperand(operands[i]);
    }
    uint32_t newInst = commonAppend(tmp,bci);
    for(uint32_t i = 0; i < opargCount; ++i)
    {
        commonAddUse(operands[i],newInst);
    }
    return newInst;
}

uint32_t GraphBuilder::buildSet(uint32_t opargCount,uint32_t * operands,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new BuildSet);

    for(uint32_t i = 0; i < opargCount; ++i)
    {
        ASSERT_OPERAND_VALID(operands[i]);
        tmp->addOperand(operands[i]);
    }
    uint32_t newInst = commonAppend(tmp,bci);
    for(uint32_t i = 0; i < opargCount; ++i)
    {
        commonAddUse(operands[i],newInst);
    }
    return newInst;
}

uint32_t GraphBuilder::buildMap(uint32_t opargCount,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new BuildMap(opargCount));
    return commonAppend(tmp,bci);
}

void GraphBuilder::storeMap(uint32_t operand1,uint32_t operand2, uint32_t operand3,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    ASSERT_OPERAND_VALID(operand3);
    std::unique_ptr<Instruction> tmp(new StoreMap(operand1,operand2,operand3));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
    commonAddUse(operand3,newInst);
}

void GraphBuilder::mapAdd(uint32_t operand1,uint32_t operand2,uint32_t operand3,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    ASSERT_OPERAND_VALID(operand3);
    std::unique_ptr<Instruction> tmp(new MapAdd(operand1,operand2,operand3));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
    commonAddUse(operand3,newInst);
}

uint32_t GraphBuilder::loadAttr(uint32_t operand,uint32_t oparg,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new LoadAttr(operand,oparg));
    uint32_t newInst = commonAppend(tmp,bci);
    return commonAddUse(operand,newInst);
}

uint32_t GraphBuilder::compareOp(uint32_t operand1,uint32_t operand2,uint32_t oparg,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new CmpStatement(operand1,operand2,oparg));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::importName(uint32_t operand1,uint32_t operand2,uint32_t oparg,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new ImportName(operand1,operand2,oparg));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

void GraphBuilder::importStar(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new ImportStar(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

uint32_t GraphBuilder::importFrom(uint32_t operand,uint32_t oparg,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new ImportFrom(operand,oparg));
    uint32_t newInst = commonAppend(tmp,bci);
    return commonAddUse(operand,newInst);
}

void GraphBuilder::jumpForward(uint32_t oparg,uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new JumpForward(oparg));
    commonAppend(tmp,bci);
}

void GraphBuilder::jumpIfTrue(uint32_t operand,uint32_t oparg,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new JumpIfTrue(operand,oparg));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}
void GraphBuilder::jumpIfFalse(uint32_t operand,uint32_t oparg,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new JumpIfFalse(operand,oparg));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

uint32_t GraphBuilder::getIter(uint32_t operand,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new GetIter(operand));
    uint32_t newInst = commonAppend(tmp,bci);
    return commonAddUse(operand,newInst);
}

uint32_t GraphBuilder::forIter(uint32_t operand,uint32_t bciToJumpWhenExit,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand);
    std::unique_ptr<Instruction> tmp(new ForIter(operand,bciToJumpWhenExit));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand,newInst);
}

void GraphBuilder::breakLoop(uint32_t bci)
{
    std::unique_ptr<Instruction> tmp(new BreakLoop());
    commonAppend(tmp,bci);
}
std::tuple<uint32_t,uint32_t> GraphBuilder::compileSetupWith(uint32_t operand, uint32_t bci)
{
    // TO BE DONE
}
void GraphBuilder::withCleanup(uint32_t operand1,uint32_t operand2,uint32_t operand3,uint32_t operand4,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    ASSERT_OPERAND_VALID(operand3);
    std::unique_ptr<Instruction> tmp(new WithCleanup(operand1,operand2,operand3));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
    commonAddUse(operand3,newInst);
}

uint32_t GraphBuilder::callFunc(uint32_t bci)
{
    // TO BE DONE
}
uint32_t GraphBuilder::makeFunc(uint32_t codeOperand,uint32_t * operands,uint32_t operandsCount,uint32_t bci)
{
    ASSERT_OPERAND_VALID(codeOperand);
    std::unique_ptr<Instruction> tmp(new MakeFunc(codeOperand,operands,operandsCount));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(codeOperand,newInst);
    for(uint32_t i = 0; i < operandsCount; ++i)
    {
        ASSERT_OPERAND_VALID(operands[i]);
        commonAddUse(operands[i],newInst);
    }
    return newInst;
}

uint32_t GraphBuilder::makeClosure(uint32_t codeOperand,uint32_t closureOperand,uint32_t * operands,uint32_t operandsCount,uint32_t bci)
{
    ASSERT_OPERAND_VALID(codeOperand);
    std::unique_ptr<Instruction> tmp(new MakeClosure(codeOperand,closureOperand,operands,operandsCount));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(codeOperand,newInst);
    for(uint32_t i = 0; i < operandsCount; ++i)
    {
        ASSERT_OPERAND_VALID(operands[i]);
        commonAddUse(operands[i],newInst);
    }
    return newInst;
}
uint32_t GraphBuilder::buildSlice(uint32_t operand1,uint32_t operand2,uint32_t operand3,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    ASSERT_OPERAND_VALID(operand3);
    std::unique_ptr<Instruction> tmp(new BuildSlice(operand1,operand2,operand3));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    commonAddUse(operand2,newInst);
    return commonAddUse(operand3,newInst);
}

uint32_t GraphBuilder::inplaceAdd(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceAdd(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplaceSubtract(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceSubtract(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplaceMultiply(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceMultiply(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplaceDivide(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceDivide(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplaceModulo(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceModulo(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplacePower(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplacePower(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplaceLshift(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceLshift(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplaceRshift(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceRshift(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplaceAnd(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceAnd(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplaceXor(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceXor(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplaceOr(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceOr(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplaceTrueDivide(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceTrueDivide(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::inplaceFloorDivide(uint32_t operand1,uint32_t operand2,uint32_t bci)
{
    ASSERT_OPERAND_VALID(operand1);
    ASSERT_OPERAND_VALID(operand2);
    std::unique_ptr<Instruction> tmp(new InplaceFloorDivide(operand1,operand2));
    uint32_t newInst = commonAppend(tmp,bci);
    commonAddUse(operand1,newInst);
    return commonAddUse(operand2,newInst);
}

uint32_t GraphBuilder::addConst(uint32_t index,PyObject * o)
{
    std::unique_ptr<Instruction> tmp(new Const(o));
    uint32_t graphid = commonAppend(tmp,-1);
    return graphid;
}

uint32_t GraphBuilder::addPhi(uint32_t index)
{
    std::unique_ptr<Instruction> instr(new Phi(index));
    return commonAppend(instr,-1);
}

void BasicBlock::dominateFindUpper(BasicBlock * bb)
{
    if(bb->dominator() == NULL)
    {
        // that means i can assume the current bb dominates it
        this->dominate(bb);
    }
    else
    {
        // change new bb 's dominator only if its dominator doesn't dominate  this
        if(!bb->dominator()->dominates(this))
        {
            BasicBlock * commonDominator = this->findCommonDominator(bb);
            RELEASE_ASSERT(commonDominator != NULL); // must not be NULL
            bb->freeDominator();
            commonDominator->dominate(bb);
        }
    }
}

bool BasicBlock::dominates(const BasicBlock * bb) const
{
    for(auto &i :dominatee_)
    {
        if(i == bb)
        {
            return true;
        }
    }
    for(auto &i : dominatee_)
    {
        if(i->dominates(bb))
        {
            return true;
        }
    }
    return false;
}

#ifdef LOG_LEVEL
void BasicBlock::print(int (*logFunc)(const char *,...))
{
    std::ostringstream outputStream;
    outputStream << "Basic block " << id_ << ":\n\t"
                 << "dominator: " << (dominator_?dominator_->id_: -1) <<"  ;loop_header: "<< isLoopHeader_ << "\n\t"
                 << "bci start: " << bci_start_ <<"; bci end " << bci_end_ << "\n\t"
                 << "successor: [ " ;

    for(auto &i:succ_)
    {
        outputStream << i->id_ << " ";
    }
    outputStream << " ]\n\t"
                 <<"predecessor: [ " ;

    for(auto &i:pred_)
    {
        outputStream << i->id_ << " ";
    }
    outputStream << " ]\n\t"
                 <<"dominatee: [ " ;

    for(auto &i:dominatee_)
    {
        outputStream << i->id_ << " ";
    }

    outputStream << " ]\n\t"
                 <<"phi desc: [ \n\t";

    for(auto &i:phi_desc_list_)
    {
        outputStream << "[ index : " << i.index_
                     << "\n\tbci : [ ";
        for(auto &j:i.from_bci_ )
        {
            outputStream << j << " ";
        }
        outputStream <<" ]\n\tparent : [ ";
        for(auto &j:i.from_parent_ )
        {
            outputStream << j << " ";
        }
        outputStream <<"]\n\t]\n\t";
    }
    outputStream << "\n\t]";

    logFunc(outputStream.str().c_str());
}
#endif

static bool comparePhiDesc(const PhiDesc & desc,uint32_t index)
{
    return desc.index_ < index;
}

void BasicBlock::addPhiFor(int index,uint32_t bci)
{
    auto insertPoint = std::lower_bound(phi_desc_list_.begin(),phi_desc_list_.end(),index,comparePhiDesc);
    if(insertPoint == phi_desc_list_.end() || insertPoint->index_ > index)
    {
        insertPoint = phi_desc_list_.insert(insertPoint,PhiDesc());
        insertPoint->index_ = index;
    }
    DEBUG_ASSERT(insertPoint->index_ == index);
    insertPoint->from_bci_.push_back(bci);
}

void BasicBlock::addPhiFromPhi(int index,int bbId)
{
    auto insertPoint = std::lower_bound(phi_desc_list_.begin(),phi_desc_list_.end(),index,comparePhiDesc);
    if(insertPoint == phi_desc_list_.end() || insertPoint->index_ > index)
    {
        insertPoint = phi_desc_list_.insert(insertPoint,PhiDesc());
        insertPoint->index_ = index;
    }
    DEBUG_ASSERT(insertPoint->index_ == index);
    insertPoint->from_parent_.push_back(bbId);
}

void BasicBlock::freePredecessor(BasicBlock * bb)
{
    auto i = std::find(bb->successor().begin(),bb->successor().end(),this);
    RELEASE_ASSERT(i !=  bb->successor().end());
    bb->successor().erase(i);
    auto j = std::find(predecessor().begin(),predecessor().end(),bb);
    RELEASE_ASSERT(j !=  predecessor().end());
    predecessor().erase(j);
}

void BasicBlock::freeDominator()
{
    auto i = std::find(this->dominator()->dominatee_.begin(),this->dominator()->dominatee_.end(),this);
    RELEASE_ASSERT(i !=  this->dominator()->dominatee_.end());
    this->dominator()->dominatee_.erase(i);
    this->dominator_ = NULL;
}

bool LocalDesc::recordLocalStore(int index,uint32_t bci)
{
    outputBitmap_.set(index);
    storeLocation_[index] = bci;
    return true;
}

uint32_t LocalDesc::getLastStoreLocation(int index)
{
    DEBUG_ASSERT(storeLocation_.find(index) != storeLocation_.end());
    return storeLocation_[index];
}

}

